#include "hash_table.h"
#include "spisok.h"
#include<stdlib.h>
#include<stdio.h>
#include<conio.h>
#include<string.h>




void ht_init(HashTable *ht, size_t size, HashFunction hf, Destructor dtor)
{
        int i;
        ht->size=size;
        ht->table=(SList**)malloc(size*sizeof(SList*));
                
                for(i=0;i<ht->size;i++)
                {
                                
                 ht->table[i]=NULL;
                        

                        
                }
        if(hf!=NULL)
        {
                ht->hashfunc=hf;
                
        }
        else
        {
                ht->hashfunc=jenkins_one_at_a_time_hash;
        }

        
                ht->dtor=dtor;
        
       
        
}
void _f(Pointer data, Pointer user_data)
{
        struct Note* t;
        HashTable* ht;
        ht=user_data;
        t=data;
        
        free(t);
}

void ht_destroy(HashTable *ht)
{
        SList* t;
        SList* ptr;
                struct Note* n;
                int i=0;
                        for(i=0;i<ht->size;i++)
                        {
                                t=ht->table[i];
                                if(t!=NULL)
                                {
                                        slist_foreach(t, _f,ht);
                                        free(t);
                                }


                        }
      
          
                        free(ht->table);

}

int _set(Pointer data, Pointer user_data)
{
   struct Note * pCurrent = data;
  struct Note * pSet = user_data;

        if ( strcmp(pCurrent->key, pSet->key) == 0 )
        {
                pCurrent->value = pSet->value;
                return 0;
        }
        return 1;

}

void ht_set(HashTable *ht, char *key, Pointer data)
{
        unsigned index;
        SList*t;
                struct Note newNote;

                newNote.value = data;
                strcpy(newNote.key, key);
                newNote.h = ht->hashfunc(key);
        
        index = newNote.h % ht->size;
        t = ht->table[index];
                if(t==NULL)
				{
					 struct Note * pNote = malloc(sizeof(struct Note));

                        pNote->value = data;
                        strcpy(pNote->key, key);
                        pNote->h = ht->hashfunc(key);

                ht->table[index] = slist_append(ht->table[index], pNote);
				}
                if ( slist_foreach(t, _set, &newNote))
                {
                        struct Note * pNote = malloc(sizeof(struct Note));

                        pNote->value = data;
                        strcpy(pNote->key, key);
                        pNote->h = ht->hashfunc(key);

                ht->table[index] = slist_append(ht->table[index], pNote);
                }

}
int _get(Pointer data, Pointer user_data)
{
        struct Note* n;
        n=data;
        if(strcmp(n->key,user_data) == 0)
        {
                return 1;
        }
        return 0;
}
Pointer ht_get(HashTable *ht, char *key)
{
        size_t index;
        SList * t;
        struct Note* n;
        index=ht->hashfunc(key) % ht->size;
        t = ht->table[index];
        n=slist_find_custom(t,key,_get);
                return n->value;
}

int _has(Pointer data, Pointer user_data)
{
        struct Note* n;
        n=data;
        if(strcmp(n->key, user_data)==0)
        {
                return 1;
        }
        return 0;
}

int ht_has(HashTable *ht, char *key)
{
        size_t index;
        SList * t;
                struct Note* n;

        index    = ht->hashfunc(key) % ht->size;
        t = ht->table[index];
               if( !slist_foreach(t, _has, key))
			   {
				   return 0;
			   }
			   else
			   {
				   return 1;
			   }
}

int _del(Pointer data, Pointer user_data)
{
        struct Note* n;
        n=data;
        if(strcmp(n->key,user_data)==0)
        {
                return 1;
                
        }
                return 0;


}
void ht_delete(HashTable *ht, char *key)
{
        int i;
        unsigned index;
        SList*t;
        SList* ptr;
        struct Note* n;
        index=ht->hashfunc(key)% ht->size;
        
        
                t=ht->table[index];
                n=slist_remove_custom(t,key,_del);
                                ht->dtor(n->value);
                                free(n);
}


void ht_traverse(HashTable *ht, void (*func)(char *key, Pointer data))
{
        size_t i;
        SList * t;
                struct Note* n;

        for(i = 0; i < ht->size; i++)
        {
          t = ht->table[i];
          n=slist_find_custom(t,1,0);
                  func(n->key,n->value);

        }
}



unsigned jenkins_one_at_a_time_hash(char *key) 
{
    unsigned hash = 0;

    for (; *key; ++key) {
        hash += *key;
        hash += (hash << 10);
        hash ^= (hash >> 6);
    }
    hash += (hash << 3);
    hash ^= (hash >> 11);
    hash += (hash << 15);
    return hash;
}

Pointer _write_to_file(Pointer data, Pointer user_data){
        struct Note * pNote;

        pNote = (struct Note *)(data);
        fprintf((FILE *)user_data, "%s %d\n", pNote->key, (int)(pNote->value));       
        return NULL;
}
void write_to_file(HashTable *ht, FILE *pFile){
        SList *list;
        size_t i;

        for(i = 0; i < ht->size; i++){
                list = ht->table[i];
                slist_foreach(list, &_write_to_file, pFile);
        }
}